﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace EfemSim
{
    public partial class EfemForm : Form
    {
        Efem efem;
        Control[] locks, docks, opens;

        PictureBox[] arms;
        PictureBox[] stages;
        Control[] carriers;
        PictureBox aligner;

        Image Wafer => Properties.Resources.wafer;

        public EfemForm()
        {
            this.arms = new PictureBox[2];
            this.stages = new PictureBox[3];
            InitializeComponent();
            this.chbCarrierLP1.Checked = this.chbCarrierLP2.Checked = true;
            this.wafer1.DataBindings.Add("Visible", this.chbCarrierLP1, "Checked");
            this.wafer2.DataBindings.Add("Visible", this.chbCarrierLP2, "Checked");
            this.carriers = new Control[] { this.panel5, this.panel6 };
            Color bc = Color.LightGray;
            for (int i = 1; i < 26; i++)
            {
                this.panel5.Controls.Add(new Label() { Text = i.ToString("d2"), Height = 18, BackColor = bc, BorderStyle = BorderStyle.FixedSingle, Dock = DockStyle.Top, TextAlign = ContentAlignment.MiddleCenter });
                this.panel6.Controls.Add(new Label() { Text = i.ToString("d2"), Height = 18, BackColor = bc, BorderStyle = BorderStyle.FixedSingle, Dock = DockStyle.Top, TextAlign = ContentAlignment.MiddleCenter });
            }
            string[] errors = new[] { "None", "VAC", "AIR", "FFU Alarm" };
            RadioButton rb;
            for (int i = 0; i < errors.Length; i++)
            {
                this.flowLayoutPanel1.Controls.Add(rb = new RadioButton() { Text = errors[i], TabIndex = i });
                rb.CheckedChanged += this.OnRadioCheckedChanged;
            }

            var gp = new System.Drawing.Drawing2D.GraphicsPath();
            gp.AddEllipse(3, 3, 12, 12);
            var reg = new Region(gp);
            this.litDockLP1.Region = this.litLockLP1.Region = this.litOpenLP1.Region =
            this.litDockLP2.Region = this.litLockLP2.Region = this.litOpenLP2.Region = reg;

            var gp1 = new System.Drawing.Drawing2D.GraphicsPath();
            gp1.AddEllipse(3, 3, 44, 44);
            var reg1 = new Region(gp1);
            this.wafer2.Region = this.wafer1.Region = reg1;

            this.wafer2.Enabled = this.wafer1.Enabled = false;

            this.locks = new Control[] { this.litLockLP1, this.litLockLP2 };
            this.docks = new Control[] { this.litDockLP1, this.litDockLP2 };
            this.opens = new Control[] { this.litOpenLP1, this.litOpenLP2 };

            this.efem = new Efem();
            this.efem.MessageArrived += this.OnAppendLog;
            this.efem.Stopped += this.OnListenerErrorOrStopped;
            this.efem.AxisYMoved += OnAxisYMoved;

            this.efem.LoadPortLocked += OnLoadPortLocked;
            this.efem.LoadPortDocked += OnLoadPortDocked;
            this.efem.LoadPortOpened += OnLoadPortOpened;
            this.efem.LoadPortDocking += OnLoadPortDocking;

            this.efem.LoadPortWaferGot += OnLoadPortWaferGot;
            this.efem.ArmGoInsideLoadPort += OnArmGoInsideLoadPort;
            this.efem.RobotGotoLoadPort += OnRobotGotoLoadPort;

            this.efem.AlignerWaferGot += OnAlignerWaferGot;
            this.efem.ArmGoInsideAligner += OnArmGoInsideAligner;
            this.efem.RobotGotoAligner += OnRobotGotoAligner;

            this.efem.StageWaferGot += OnStageWaferGot;
            this.efem.ArmGoInsideStage += OnArmGoInsideStage;
            this.efem.RobotGotoStage += OnRobotGotoStage;

            this.efem.AlignerMoving += OnAlignerMoving;
            this.efem.AlignerRotating += OnAlignerRotating;

            this.efem.LoadPortMappingChanged += OnLoadPortMappingChanged;
        }

        #region EFEM 运动事件

        private void OnLoadPortMappingChanged(int index, bool[] slots)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<int, bool[]>(this.OnLoadPortMappingChanged), index, slots);
                return;
            }
            var wafers = this.carriers[index - 1].Controls;
            Color bc = Color.FromArgb(171, 213, 245);
            for (int i = 0; i < 25; i++)
            {
                wafers[i].BackColor = slots[i] ? bc : Color.LightGray;
            }
        }

        private void OnAlignerRotating(float angle)
        {
            Bitmap img = new Bitmap(155, 155);
            Graphics g = Graphics.FromImage(img);
            g.TranslateTransform(72, 72);
            g.RotateTransform(angle);
            g.DrawImage(Properties.Resources.wafer, -72, -72);
            this.Invoke(new Action(() => this.aligner.Image = img));
        }

        private void OnAlignerMoving(float rate)
        {
        }

        private void OnAxisYMoved(bool wafer1, bool wafer2, float rate)
        {
            this.Invoke(new Action(() =>
            {
                if (wafer1) this.MoveWaferTop(ref this.arms[0], 118, 288, rate);
                if (wafer2) this.MoveWaferTop(ref this.arms[1], 118, 288, rate);
            }));
        }

        private void OnRobotGotoStage(int stage, int arm, bool wafer1, bool wafer2, float rate)
        {
            if (arm == 0) return;
            this.Invoke(new Action(() =>
            {
                if (stage == 1)
                {
                    this.MoveWaferLeft(ref this.arms[arm - 1], 191, -72, rate);
                }
                else
                {
                    this.MoveWaferTop(ref this.arms[arm - 1], 118, 288, rate);
                }
            }));
        }

        private void OnArmGoInsideStage(int stage, int arm, bool wafer1, bool wafer2, float rate)
        {
            this.Invoke(new Action(() =>
            {
                if (stage == 1)
                {
                    this.MoveWaferLeft(ref this.arms[arm - 1], 119, -62, rate);
                }
                else
                {
                    this.MoveWaferTop(ref this.arms[arm - 1], 118, -72, rate);
                }
            }));
        }

        private void OnStageWaferGot(int stage, int arm, bool got) => this.SwapWafer(ref this.stages[stage - 1], ref this.arms[arm - 1]);

        private void OnRobotGotoAligner(int arm, bool wafer1, bool wafer2, float rate) => this.Invoke(new Action(() => this.MoveWaferTop(ref this.arms[arm - 1], 118, 288, rate)));

        private void OnArmGoInsideAligner(int arm, bool wafer1, bool wafer2, float rate) => this.Invoke(new Action(() => this.MoveWaferTop(ref this.arms[arm - 1], 405, 72, rate)));

        private void OnAlignerWaferGot(int arm, bool got) => this.SwapWafer(ref this.aligner, ref this.arms[arm - 1]);

        private void OnRobotGotoLoadPort(int arm, bool wafer1, bool wafer2, float rate) => this.Invoke(new Action(() => this.MoveWaferLeft(ref this.arms[arm - 1], 191, 72, rate)));

        private void OnArmGoInsideLoadPort(int lp, int arm, bool wafer1, bool wafer2, float rate) => this.Invoke(new Action(() => this.MoveWaferLeft(ref this.arms[arm - 1], 263, 56, rate)));

        private void OnLoadPortWaferGot(int lp, int slot, int arm, bool got)
        {
            if (got)
            {
                int top = 333;
                if (lp == 2) top -= 144;
                this.Invoke(new Action(() => this.GotWafer(ref top, ref this.arms[arm - 1])));
            }
            else
            {
                this.Invoke(new Action(() => this.PutWafer(ref this.arms[arm - 1])));
            }
        }

        private void OnLoadPortOpened(int index, bool opened)
        {
            index--;
            this.opens[index].BackColor = opened ? Color.LightGreen : Color.SlateGray;
        }

        private void OnLoadPortDocking(int index, bool dock, float rate)
        {
            if (index == 1)
            {
                this.Invoke(new Action(() => this.wafer1.Left = 358 - (int)(38 * rate)));
            }
            else
            {
                this.Invoke(new Action(() => this.wafer2.Left = 358 - (int)(38 * rate)));
            }
        }

        private void OnLoadPortDocked(int index, bool docked)
        {
            index--;
            this.docks[index].BackColor = docked ? Color.LightGreen : Color.SlateGray;
        }

        private void OnLoadPortLocked(int index, bool locked)
        {
            index--;
            this.locks[index].BackColor = locked ? Color.LightGreen : Color.SlateGray;
        }

        #endregion

        #region EFEM 事件辅助函数

        void GotWafer(ref int top, ref PictureBox arm)
        {
            var img = this.CreateWafer();
            img.Left = 358;
            img.Top = top;
            img.Show();
            arm = img;
            this.panel1.Controls.Add(img);
            img.BringToFront();
        }
        void PutWafer(ref PictureBox arm)
        {
            arm.Hide();
            this.panel1.Controls.Remove(arm);
            arm = null;
        }
        void SwapWafer(ref PictureBox src, ref PictureBox dst)
        {
            PictureBox pb = src;
            src = dst;
            dst = pb;
        }
        void MoveWaferLeft(ref PictureBox arm, int left, int range, float rate)
        {
            if (arm == null) return;
            arm.Left = left + (int)(range * rate);
        }
        void MoveWaferTop(ref PictureBox arm, int top, int range, float rate)
        {
            if (arm == null) return;
            arm.Top = top + (int)(range * rate);
        }

        #endregion

        // 监听异常，或者停止运行
        void OnListenerErrorOrStopped(bool errorOrstopped)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke((Action<bool>)this.OnListenerErrorOrStopped, errorOrstopped);
                return;
            }

            if (this.txtIP.Enabled) return;
            this.panel3.Enabled = this.panel4.Enabled = true;
            this.txtIP.Enabled = this.nudPort.Enabled = this.btnStart.Enabled = true;
            this.btnStop.Enabled = false;
        }
        // 启动按钮
        private void btnStart_Click(object sender, EventArgs e)
        {
            if (!IPAddress.TryParse(this.txtIP.Text, out IPAddress ip))
            {
                MessageBox.Show("请填写正确的IP地址");
                this.txtIP.Focus();
                return;
            }
            try
            {
                this.efem.SetCarrier(1, this.chbCarrierLP1.Checked);
                this.efem.SetCarrier(2, this.chbCarrierLP2.Checked);
                this.efem.SetAdapter(1, this.chbLP1.Checked);
                this.efem.SetAdapter(2, this.chbLP2.Checked);
                this.efem.Start(ip, (int)this.nudPort.Value);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                this.nudPort.Focus();
                return;
            }

            this.panel3.Enabled = this.panel4.Enabled = false;
            this.txtIP.Enabled = this.nudPort.Enabled = this.btnStart.Enabled = false;
            this.btnStop.Enabled = true;
        }
        // 停止按钮
        private void btnStop_Click(object sender, EventArgs e) => this.efem.Stop();
        // 清除日志
        private void btnCLear_Click(object sender, EventArgs e) => this.txtLog.Clear();
        // 事件注入
        private void btnInjectEvent_Click(object sender, EventArgs e)
        {
            if (this.comboBox1.SelectedIndex < 0) return;
            if (string.IsNullOrWhiteSpace(this.textBox1.Text))
            {
                MessageBox.Show("请输入事件的值");
                this.textBox1.Focus();
                return;
            }
            this.efem.InjectEvent(this.comboBox1.Text, this.textBox1.Text);
        }
        // IO 注入
        private void OnRadioCheckedChanged(object sender, EventArgs e)
        {
            RadioButton rb = sender as RadioButton;
            if (!rb.Checked) return;
            this.efem.InjectError(rb.TabIndex);
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);
            if (m.Msg == 0x84)
            {
                switch (m.Result.ToInt32())
                {
                    case 12:
                    case 13:
                    case 14:
                    case 15:
                    case 16:
                    case 17:
                        m.Result = new IntPtr(0);
                        break;
                }

            }
        }

        void OnAppendLog(bool? sent, string line) => this.Invoke(new Action<string, bool?>(this.OnSyncAppendLog), line, sent);

        void OnSyncAppendLog(string line, bool? sent)
        {
            this.txtLog.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffffff ：")}{(sent == null ? "" : sent == true ? "[send]" : "[recv]")}\t{line}\r\n");
            if (this.txtLog.SelectionStart < this.txtLog.TextLength) return;
            this.txtLog.SelectionStart = this.txtLog.TextLength;
            this.txtLog.ScrollToCaret();
        }
        PictureBox CreateWafer() => new PictureBox() { Visible = false, Image = this.Wafer, SizeMode = PictureBoxSizeMode.Zoom, Width = 50, Height = 50, Region = this.wafer2.Region };
    }
}
